﻿using System;
using System.IO.Ports;
using Communication.Lib.Core;
using log4net;

namespace Communication.Lib.Communication
{
    /// <summary>
    /// 用于同某Com口收发消息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SerialPortCommunicate<T> : ICommunicatable<T> where T : BaseInfo
    {
        private ILog _logger = LogManager.GetLogger(typeof(SerialPortCommunicate<T>));

        private SerialPort _serialPort;

        private DateTime _lastAliveTime;

        private IProtocolable<T> protocol;

        #region 属性

        /// <summary>
        /// 串口名称
        /// </summary>
        public string PortName { get; }

        /// <summary>
        /// 波特率
        /// </summary>
        public int BaudRate { get; }

        /// <summary>
        /// 数据长度
        /// </summary>
        public int DataBits { get; }

        /// <summary>
        /// 校验位
        /// </summary>
        public Parity Parity { get; }

        /// <summary>
        /// 停止位
        /// </summary>
        public StopBits StopBits { get; }

        /// <summary>
        /// 解析接收数据使用的协议
        /// </summary>
        public IProtocolable<T> Protocol => protocol;

        #endregion 属性

        public event Action<T> RecvHandler;

        public bool IsAlive => (DateTime.Now - _lastAliveTime).TotalSeconds <= 30;

        public SerialPortCommunicate(string com, int bauRate, IProtocolable<T> protocol, int dataBits = 8,
            Parity parity = Parity.None, StopBits stopBits = StopBits.One)
        {
            _serialPort = new SerialPort(com);
            PortName = com;
            this.protocol = protocol;
            _serialPort.BaudRate = BaudRate = bauRate;
            _serialPort.DataBits = DataBits = dataBits;
            _serialPort.Parity = Parity = parity;
            _serialPort.StopBits = StopBits = stopBits;

            _serialPort.Handshake = Handshake.None;//默认无握手协议

            _serialPort.DataReceived += _serialPort_DataReceived;

            _lastAliveTime = DateTime.Now;
        }

        private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                var length = _serialPort.BytesToRead;
                if (length == 0) return;

                var tempbuffer = new byte[length];
                var read = _serialPort.Read(tempbuffer, 0, tempbuffer.Length);
                if (read != length) _logger.Warn($"[{protocol.GetType().Name}] Received {length} ,but read {read}");

                protocol.AddBytes(tempbuffer);
                var obj = protocol.DecodeData();
                while (obj != null)
                {
                    RecvHandler?.Invoke(obj);
                    _lastAliveTime = DateTime.Now;
                    obj = protocol.DecodeData();
                }
            }
            catch (Exception ex)
            {
                _logger.Error($"UnKnownError. msg:{ex}");
            }
        }

        public bool SendData(T info)
        {
            if (!_serialPort.IsOpen)
            {
                return false;
            }

            try
            {
                var data = protocol.EncodeBytes(info);

                return SendData(data);
            }
            catch (Exception e)
            {
                _logger.Error($"[{nameof(SerialPortCommunicate<T>)}] SendDataError.data:{info.ToJson()}. ex:{e}");
            }

            return false;
        }

        public bool SendData(byte[] bytes)
        {
            if (bytes == null || bytes.Length <= 0)
            {
                return false;
            }
            try
            {
                _serialPort.Write(bytes, 0, bytes.Length);
                _lastAliveTime = DateTime.Now;
                return true;
            }
            catch (Exception ex)
            {
                _logger.Error($"[{nameof(SerialPortCommunicate<T>)}] SendDataError.data:{bytes.FormatAsHex()}. ex:{ex}");
            }

            return false;
        }

        public void Start()
        {
            if (!_serialPort.IsOpen)
            {
                _serialPort.Open();
            }
        }

        public void Stop()
        {
            if (_serialPort == null)
            {
                return;
            }

            if (_serialPort.IsOpen)
            {
                _serialPort.Close();
            }
        }

        /// <summary>
        /// 故障时 重新初始化
        /// </summary>
        /// <returns></returns>
        public bool ReInit()
        {
            try
            {
                _serialPort?.Dispose();
                _serialPort = new SerialPort(PortName);
                _serialPort.DataReceived += _serialPort_DataReceived;
                _serialPort.BaudRate = BaudRate;
                _serialPort.DataBits = DataBits;
                _serialPort.StopBits = StopBits;
                _serialPort.Parity = Parity;

                _serialPort.Handshake = Handshake.None; //握手协议
                _serialPort.Open();
                return true;
            }
            catch (Exception ex)
            {
                //todo 记录日志 以及处理
                _logger.Error($"ReInit Error.msg:{ex}");
                _serialPort = null;
                return false;
            }
        }
    }
}